Technical Q&A TB42
Pop-up Menu CDEFs: The Real Story

Q: We need a handle to a pop-up menu in a dialog so that we can dynamically change its items. I tried to set the menu ID to -12345 and stuff a dynamically allocated menu handle into the pop-up control's "private" data field, but this menu does not appear. Please advise. I have also had problems with disposing a dialog which contains a pop-up menu control if there are other instances of the same dialog, because disposing the first dialog disposes the menu handle, and the other dialogs can no longer use it. I've attempted (without success) to circumvent this by NULLing the menu reference in the control prior to disposing the dialog. Does the control create another reference to the menu I am unable to get at? Is there any way around this issue? Basically, how do I retain my reference to my menu after closing the window?

P.S. I need solutions that work regardless of whether the Appearance extension is installed.

A: Your question reads as if you are having a number of problems. Fortunately, there is only one. Here is some little-known wisdom regarding the pop-up menu CDEFs (both popupMenuProc and kControlPopupButtonProc):


IMPORTANT:
Never allow the pop-up menu CDEFs to create a menu for you.


What should you do instead?

  1. Create a menu with NewMenu or GetMenu.

  2. Populate the menu however you like.

  3. Insert the menu into the menu list with InsertMenu. (Remember to pass hierMenu in the second parameter, since pop-up menus go into the "hierarchical" portion of the menu list. Don't think too hard about this terminology or you'll embarrass whoever invented it.)

  4. Create the relevant dialog or control with GetNewDialog, NewDialog, GetNewControl, or NewControl.

  5. Do something with the dialog or control, e.g. call ModalDialog or DialogSelect or TrackControl or HandleControlClick. Repopulate the menu as you please, but remember to reset the control's maximum setting if you change the number of items in the menu, and redraw the control (invalidate it or call Draw1Control) if you change the menu item which represents the current value.

  6. Dispose the dialog or control with DisposeDialog or DisposeControl.

  7. Delete the menu from the menu bar with DeleteMenu.

  8. Dispose the menu with DisposeMenu.

How and why does this help? The pop-up menu CDEFs look in two places for the menu you specify.

  • First, they look in the menu list for a menu with the given menu ID by calling GetMenuHandle. If they find a menu this way, they behave rationally and predictably: they assume you will be responsible for deleting and disposing the menu. Thus, the menu will persist after the control is disposed and multiple instances of the control will not cause you trouble. The other advantage is that you already have the menu handle, so there is no need to go poking through obscure data hanging off the control record.

  • If no menu with the given ID can be found in the menu list, the CDEFs call GetMenu to find and load a 'MENU' resource. They insert and delete the resulting menu into and from the menu list as necessary, and they dispose the menu when the control is disposed. (They also go to great lengths to allow the menu to be purged at certain times, which can cause headaches which are beyond the scope of this document.)

We recommend you always make sure the CDEFs can find the menus they want in the menu list by using the numbered steps listed above. If you don't follow these steps, the CDEFs will create and manage the menus, the problems you describe will crop up, and things will get tedious very quickly. The only time you want to allow the CDEFs to create a menu is when you know the menu will only be used in a single (optimally moveable) modal dialog. If you'd rather not keep this exception in mind, just follow the numbered steps; they always work.

By the way, yes, the CDEFs do cache a reference to the menu in a place other than their "private" control data, so changing that data will not help. In fact, altering system data structures is a good thing to avoid in general. Read when you must, but don't ever write if you can possibly avoid it. Also, take a look at what the Mac OS 8 Control Manager has to say about the Pop-Up Menu Private Structure(PopupPrivateData). Executive summary: Use the accessor functions, Luke!

[Jul 30 1998]


Developer Documentation | Technical Notes | Development Kits | Sample Code